<!DOCTYPE html>
<html>
<head>
  <title>Multiplayer</title>
  <script src="../../kontra.js"></script>
</head>
<body>
  <div><b>NOTE:</b>Gamepad support requires using a secure context (HTTPS).</div>
  <canvas id="game" width="600" height="400" style="background: #333331"></canvas>
  <script id="code">
    // initialize the game and setup the canvas
    let { canvas, context } = kontra.init();

     // initialize gamepad events
    kontra.initGamepad();

    // set image path so we don't have to reference the image by it's path
    kontra.setImagePath('../imgs/');

    // load the images
    kontra.load('bulletBlue1.png', 'bulletGreen1.png', 'bulletRed1.png', 'bulletSand1.png', 'tank_blue.png', 'tank_green.png', 'tank_red.png', 'tank_sand.png').then(() => {

      // default text settings
      let textConfig = {
        font: '20px Arial',
        color: 'white',
        textAlign: 'center',
        anchor: { x: 0.5, y: 0.5 }
      }

      let playerColors = ['blue', 'green', 'red', 'sand'];
      let currentScene;

      // --------------------------------------------------
      // lobby scene
      // --------------------------------------------------
      
      // display a white square if player has not joined or a filled in colored
      // square if the player has joined
      let playerSquareConfig = {
        render() {
          if (this.joined) {
            this.context.fillStyle = playerColors[this.index];
            this.context.fillRect(0, 0, this.width, this.height);
          }
          else {
            this.context.strokeStyle = 'white';
            this.context.strokeRect(0, 0, this.width, this.height);          
          }
        }
      };

      let lobbyObjects = [];
      let players = [];

      // create 4 player lobby seats
      for (let i = 0; i < 4; i++) {
        let x = 100 + 125 * i;

        let playerSquare = kontra.Sprite({
          ...playerSquareConfig,
          x, 
          y: 200,
          width: 100,
          height: 150,
          anchor: { x: 0.5, y: 0.5 },
          index: i,
          joined: i === 0 ? true : false
        });

        // player number beneath each lobby seat
        let playerText = kontra.Text({
          ...textConfig,
          x,
          y: playerSquare.y + playerSquare.height / 2 + 15,
          text: `Player ${i + 1}`
        });

        // text if the player hasn't joined
        playerSquare.joinText = kontra.Text({
          ...textConfig,
          id: 'join',
          index: i,
          lineHeight: 1.25,
          text: 'Press\nA\nto Join',
        });

        // text if the player has joined
        playerSquare.readyText = kontra.Text({
          ...textConfig,
          id: 'ready',
          index: i,
          lineHeight: 1.25,
          text: 'Ready!'
        });

        // player 1 starts joined
        playerSquare.addChild(i === 0 ? playerSquare.readyText : playerSquare.joinText);

        lobbyObjects.push(playerSquare, playerText);
        players.push(playerSquare);
      }

      let startText = kontra.Text({
        ...textConfig,
        font: '24px Arial',
        x: canvas.width / 2,
        y: canvas.height - 25,
        text: 'Press Start to Play'
      });
      lobbyObjects.push(startText);

      // create the lobby scene and begin gamepad event listeners
      let lobbyScene = kontra.Scene({
        id: 'lobby',
        objects: lobbyObjects,
        onShow() {
          kontra.onGamepad('south', (gamepad) => {
            // show a player joined the game
            if (gamepad.index > 0) {
              players[gamepad.index].joined = true;
              players[gamepad.index].children = [players[gamepad.index].readyText];
            }
          });
          kontra.onGamepad('start', () => {
            // change scenes and start the game
            lobbyScene.hide();
            gameScene.show();
            currentScene = gameScene;
          });
        },
        onHide() {
          kontra.offGamepad('south');
          kontra.offGamepad('start');
        }
      });

      // --------------------------------------------------
      // game scene
      // --------------------------------------------------
      let sprites = [
        // start with 4 walls
        // left side
        kontra.Sprite({ 
          x: 75, 
          y: 150, 
          width: 25, 
          height: canvas.height - 300, 
          color: 'white',
          type: 'wall'
        }),
        // right side
        kontra.Sprite({ 
          x: canvas.width - 100, 
          y: 150, 
          width: 25, 
          height: canvas.height - 300, 
          color: 'white',
          type: 'wall'
        }),
        // top side
        kontra.Sprite({ 
          x: 175, 
          y: 75, 
          width: canvas.width - 350, 
          height: 25, 
          color: 'white',
          type: 'wall'
        }),
        // bottom side
        kontra.Sprite({ 
          x: 175, 
          y: canvas.height - 100, 
          width: canvas.width - 350, 
          height: 25, 
          color: 'white',
          type: 'wall'
        }),
      ];

      // player start locations and rotation
      let startInfo = [
        // left side
        { x: 40, y: canvas.height / 2, rotation: kontra.degToRad(90) },
        // right side
        { x: canvas.width - 40, y: canvas.height / 2, rotation: -kontra.degToRad(90) },
        // top side
        { x: canvas.width / 2, y: 40, rotation: kontra.degToRad(180) },
        // bottom side
        { x: canvas.width / 2, y: canvas.height - 40, rotation: 0 }
      ];

      // modified collision detection to ignore rotation
      function collidesIgnoreRotation(obj1, obj2) {
        let obj1Rot = obj1.rotation;
        let obj2Rot = obj2.rotation;

        obj1.rotation = 0;
        obj2.rotation = 0;

        let collides = kontra.collides(obj1, obj2);

        obj1.rotation = obj1Rot;
        obj2.rotation = obj2Rot;

        return collides;
      }

      // add each player to the game
      function addPlayers() {
        players.filter(player => player.joined).forEach(player => {
          let index = player.index;
          let color = playerColors[index];
          let capColor = color.charAt(0).toUpperCase() + color.substr(1);
          let { x, y, rotation } = startInfo[index];

          let playerSprite = kontra.Sprite({
            x,
            y,
            rotation,
            index,
            anchor: { x: 0.5, y: 0.5 },
            type: 'tank',
            // shrink the tank and flip it along the y axis
            scaleX: 0.5,
            scaleY: -0.5,
            // keep track of number of frames so we can limit firing speed
            tick: 0,
            image: kontra.imageAssets[`tank_${color}`],
            update() {
              this.tick++;

              // get the value of the left gamepad stick
              let axisX = kontra.gamepadAxis('leftstickx', index);
              let axisY = kontra.gamepadAxis('leftsticky', index);
              let x = 0;
              let y = 0;

              // move player by stick
              if (axisX < -0.4) {
                x = -1;
              }
              else if (axisX > 0.4) {
                x = 1;
              }
              if (axisY < -0.4) {
                y = -1;
              }
              else if (axisY > 0.4) {
                y = 1;
              }

              // face player in the direction of movement
              if (x || y) {
                this.rotation = kontra.angleToTarget(this, { x: this.x + x, y: this.y + y });
              }

              let newX = this.x + x;
              let newY = this.y + y;

              // prevent moving off the screen
              if (
                !(newX - this.width / 2 < 0 || 
                newX + this.width / 2 > canvas.width || 
                newY - this.height / 2 < 0 || 
                newY + this.height / 2 > canvas.height)
              ) {
                this.prevX = this.x;
                this.prevY = this.y;
                this.x = newX;
                this.y = newY;
              }

              // fire bullets once every 30 frames
              if (
                kontra.gamepadPressed('south', { gamepad: index }) &&
                this.tick >= 30
                ) {
                this.tick = 0;

                // start the bullet at the end of the tank sprite
                let endOfTank = kontra.movePoint(this, this.rotation, this.height / 2);

                // move bullet at a speed of 4
                let speed = kontra.movePoint({x: 0, y: 0}, this.rotation, 4);

                // create bullet
                sprites.push(kontra.Sprite({
                  x: endOfTank.x,
                  y: endOfTank.y,
                  rotation: this.rotation,
                  index,
                  type: 'bullet',
                  anchor: { x: 0.5, y: 0.5 },
                  dx: speed.x,
                  dy: speed.y,
                  image: kontra.imageAssets[`bullet${capColor}1`],
                  // bullet lives for 120 frames
                  ttl: 120
                }));
              }
            }
          });

          sprites.push(playerSprite);
        });
      }

      let gameScene = kontra.Scene({
        id: 'game',
        update() {
          sprites.forEach(sprite => {
            sprite.update();

            // check for bullet collision against any non-bullet sprites
            if (sprite.type === 'bullet') {
              sprites
                .filter(otherSprite => {
                  return otherSprite.type !== 'bullet' &&
                    // don't check against the matching player tank 
                    otherSprite.index !== sprite.index
                })
                .forEach(otherSprite => {
                  if (collidesIgnoreRotation(sprite, otherSprite)) {
                    sprite.ttl = 0;

                    // remove tank from game
                    if (otherSprite.type === 'tank') {
                      otherSprite.ttl = 0;
                    }
                  }
                });
            }

            // check for tank-wall collision and prevent player from moving
            if (sprite.type === 'tank') {
              sprites
                .filter(otherSprite => otherSprite.type === 'wall')
                .forEach(wall => {
                  if (collidesIgnoreRotation(sprite, wall)) {
                    sprite.x = sprite.prevX;
                    sprite.y = sprite.prevY;
                  }
                });
            }
          });

          sprites = sprites.filter(sprite => sprite.isAlive())
        },
        render() {
          sprites.forEach(sprite => sprite.render());
        },
        onShow() {
          addPlayers();
        }
      });

      // --------------------------------------------------
      // scene manager
      // --------------------------------------------------
      currentScene = lobbyScene;
      currentScene.show();

      let loop = kontra.GameLoop({
        update() {
          currentScene.update();
        },
        render() {
          currentScene.render();
        }
      });

      loop.start();
    });
  </script>
</body>
</html>